package org.tigris.subversion.subclipse.ui.conflicts;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.compare.CompareUI;
import org.eclipse.compare.internal.CompareAction;
import org.eclipse.compare.internal.CompareEditor;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.tigris.subversion.subclipse.core.ISVNLocalResource;
import org.tigris.subversion.subclipse.core.ISVNRemoteResource;
import org.tigris.subversion.subclipse.core.SVNException;
import org.tigris.subversion.subclipse.core.commands.GetStatusCommand;
import org.tigris.subversion.subclipse.core.commands.RevertResourcesCommand;
import org.tigris.subversion.subclipse.core.resources.SVNTreeConflict;
import org.tigris.subversion.subclipse.core.resources.SVNWorkspaceRoot;
import org.tigris.subversion.subclipse.ui.Messages;
import org.tigris.subversion.subclipse.ui.SVNUIPlugin;
import org.tigris.subversion.subclipse.ui.compare.SVNLocalCompareInput;
import org.tigris.subversion.subclipse.ui.operations.ResolveOperation;
import org.tigris.subversion.subclipse.ui.operations.ShowDifferencesAsUnifiedDiffOperationWC;
import org.tigris.subversion.subclipse.ui.wizards.SizePersistedWizardDialog;
import org.tigris.subversion.svnclientadapter.ISVNClientAdapter;
import org.tigris.subversion.svnclientadapter.ISVNConflictResolver;
import org.tigris.subversion.svnclientadapter.ISVNInfo;
import org.tigris.subversion.svnclientadapter.ISVNLogMessage;
import org.tigris.subversion.svnclientadapter.ISVNLogMessageChangePath;
import org.tigris.subversion.svnclientadapter.ISVNStatus;
import org.tigris.subversion.svnclientadapter.SVNClientException;
import org.tigris.subversion.svnclientadapter.SVNRevision;
import org.tigris.subversion.svnclientadapter.SVNStatusKind;
import org.tigris.subversion.svnclientadapter.SVNUrl;

public class ResolveTreeConflictWizard extends Wizard {
  private SVNTreeConflict treeConflict;
  private IWorkbenchPart targetPart;
  private ISVNLocalResource svnResource;

  private ResolveTreeConflictWizardMainPage mainPage;

  //	private ISVNClientAdapter svnClient;
  private ISVNStatus[] statuses;
  private boolean copiedToRetrieved;
  private boolean remoteCopiedToRetrieved;
  private ISVNStatus copiedTo;
  private ISVNStatus remoteCopiedTo;
  private ISVNLogMessage[] logMessages;
  private boolean added;
  private Exception mergeException;
  private Exception revertException;

  private boolean compare;

  private File mergePath;

  private ISVNClientAdapter svnClient;

  public ResolveTreeConflictWizard(SVNTreeConflict treeConflict, IWorkbenchPart targetPart) {
    super();
    this.treeConflict = treeConflict;
    this.targetPart = targetPart;
    svnResource = SVNWorkspaceRoot.getSVNResourceFor(treeConflict.getResource());
    try {
      added = svnResource.isAdded();
    } catch (SVNException e) {
      SVNUIPlugin.log(IStatus.ERROR, e.getMessage(), e);
    }
  }

  public void addPages() {
    super.addPages();
    setWindowTitle(Messages.ResolveTreeConflictWizard_title + treeConflict.getResource().getName());

    mainPage = new ResolveTreeConflictWizardMainPage();
    addPage(mainPage);
  }

  public boolean needsProgressMonitor() {
    return true;
  }

  public boolean performFinish() {
    if (mainPage.getReplace()) {
      mergeException = null;
      try {
        BusyIndicator.showWhile(
            Display.getDefault(),
            new Runnable() {
              public void run() {
                try {
                  svnClient = svnResource.getRepository().getSVNClient();
                  File file = svnResource.getResource().getLocation().toFile();
                  svnClient.remove(new File[] {file}, true);
                  SVNUrl url =
                      new SVNUrl(
                          treeConflict.getConflictDescriptor().getSrcRightVersion().getReposURL()
                              + "/"
                              + treeConflict
                                  .getConflictDescriptor()
                                  .getSrcRightVersion()
                                  .getPathInRepos()); //$NON-NLS-1$
                  SVNRevision revision;
                  int index =
                      treeConflict
                          .getConflictDescriptor()
                          .getSrcRightVersion()
                          .toString()
                          .lastIndexOf("@"); // $NON-NLS-1$
                  if (index == -1) {
                    revision = SVNRevision.HEAD;
                  } else {
                    long number =
                        Long.parseLong(
                            treeConflict
                                .getConflictDescriptor()
                                .getSrcRightVersion()
                                .toString()
                                .substring(index + 1));
                    revision = new SVNRevision.Number(number);
                  }
                  svnClient.copy(url, file, revision);
                } catch (Exception e) {
                  mergeException = e;
                }
              }
            });
        if (mergeException != null) {
          SVNUIPlugin.log(IStatus.ERROR, mergeException.getMessage(), mergeException);
          MessageDialog.openError(
              getShell(), Messages.ResolveTreeConflictWizard_2, mergeException.getMessage());
          return false;
        }
        svnResource.getResource().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
      } catch (Exception e) {
        SVNUIPlugin.log(IStatus.ERROR, e.getMessage(), e);
        MessageDialog.openError(getShell(), Messages.ResolveTreeConflictWizard_2, e.getMessage());
        return false;
      }
      return true;
    }
    compare = mainPage.getCompare();
    if (mainPage.getMergeFromRepository()) {
      try {
        final SVNUrl url = new SVNUrl(mainPage.getMergeFromUrl());
        SVNRevision revision1;
        if (treeConflict.getConflictDescriptor().getSrcLeftVersion().getPegRevision()
            == treeConflict.getConflictDescriptor().getSrcRightVersion().getPegRevision())
          revision1 =
              new SVNRevision.Number(
                  treeConflict.getConflictDescriptor().getSrcLeftVersion().getPegRevision() - 1);
        else
          revision1 =
              new SVNRevision.Number(
                  treeConflict.getConflictDescriptor().getSrcLeftVersion().getPegRevision());
        final SVNRevision revision2 =
            new SVNRevision.Number(
                treeConflict.getConflictDescriptor().getSrcRightVersion().getPegRevision());
        if (treeConflict.getConflictDescriptor().getSrcLeftVersion().getPegRevision()
            == treeConflict.getConflictDescriptor().getSrcRightVersion().getPegRevision())
          revision1 =
              new SVNRevision.Number(
                  treeConflict.getConflictDescriptor().getSrcLeftVersion().getPegRevision() - 1);
        final IResource mergeTarget = mainPage.getMergeTarget();
        final SVNRevision rev1 = revision1;

        svnClient = svnResource.getRepository().getSVNClient();
        mergeException = null;
        BusyIndicator.showWhile(
            Display.getDefault(),
            new Runnable() {
              public void run() {
                try {
                  mergePath = mergeTarget.getLocation().toFile();
                  svnClient.merge(url, rev1, url, revision2, mergePath, true, false, false, true);
                  try {
                    // Refresh the resource after merge
                    if (mergeTarget.getParent() != null)
                      mergeTarget
                          .getParent()
                          .refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
                    else
                      mergeTarget.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
                  } catch (CoreException e1) {
                  }
                } catch (Exception e) {
                  mergeException = e;
                }
              }
            });
        if (mergeException != null) {
          SVNUIPlugin.log(IStatus.ERROR, mergeException.getMessage(), mergeException);
          MessageDialog.openError(
              getShell(),
              Messages.ResolveTreeConflictWizard_mergeError,
              mergeException.getMessage());
          return false;
        }
      } catch (Exception e) {
        SVNUIPlugin.log(IStatus.ERROR, e.getMessage(), e);
        MessageDialog.openError(
            getShell(), Messages.ResolveTreeConflictWizard_mergeError, e.getMessage());
        return false;
      } finally {
        svnResource.getRepository().returnSVNClient(svnClient);
      }
    }
    if (mainPage.getCompare()) {

      if (mainPage.getCompareResource2() == null) {
        ISVNLocalResource svnCompareResource = mainPage.getSvnCompareResource();
        if (svnCompareResource == null) svnCompareResource = svnResource;
        ISVNRemoteResource remoteResource = mainPage.getRemoteResource();
        try {

          File file = File.createTempFile("revision", ".diff"); // $NON-NLS-1$ //$NON-NLS-2$
          file.deleteOnExit();
          File path = new File(svnCompareResource.getResource().getLocation().toString());
          SVNUrl toUrl = remoteResource.getUrl();
          SVNRevision toRevision = remoteResource.getRevision();

          ShowDifferencesAsUnifiedDiffOperationWC operation =
              new ShowDifferencesAsUnifiedDiffOperationWC(
                  targetPart, path, toUrl, toRevision, file);
          SVNLocalCompareInput compareInput =
              new SVNLocalCompareInput(svnCompareResource, remoteResource);
          compareInput.setDiffOperation(operation);

          CompareUI.openCompareEditorOnPage(compareInput, targetPart.getSite().getPage());
          CompareCloseListener closeListener =
              new CompareCloseListener(
                  Messages.ResolveTreeConflictWizard_compare
                      + svnCompareResource.getName()
                      + " <workspace>"); //$NON-NLS-1$
          targetPart.getSite().getPage().addPartListener(closeListener);
        } catch (Exception e) {
          SVNUIPlugin.log(IStatus.ERROR, e.getMessage(), e);
          MessageDialog.openError(
              getShell(), Messages.ResolveTreeConflictWizard_compareError, e.getMessage());
          return false;
        }
      } else {

        ISelection selection =
            new IStructuredSelection() {
              public Object getFirstElement() {
                return mainPage.getCompareResource1();
              }

              public Iterator iterator() {
                return toList().iterator();
              }

              public int size() {
                return 2;
              }

              public Object[] toArray() {
                IResource[] compareResources = {
                  mainPage.getCompareResource1(), mainPage.getCompareResource2()
                };
                return compareResources;
              }

              public List toList() {
                List compareList = new ArrayList();
                compareList.add(mainPage.getCompareResource1());
                compareList.add(mainPage.getCompareResource2());
                return compareList;
              }

              public boolean isEmpty() {
                return false;
              }
            };
        CompareAction compareAction = new CompareAction();
        compareAction.setActivePart(null, targetPart);
        IAction action = new Action() {};
        compareAction.selectionChanged(action, selection);
        compareAction.run(selection);
        CompareCloseListener closeListener =
            new CompareCloseListener(
                Messages.ResolveTreeConflictWizard_compare2
                    + mainPage.getCompareResource1().getName()
                    + "' - '"
                    + mainPage.getCompareResource2().getName()
                    + "')"); //$NON-NLS-1$ //$NON-NLS-2$
        targetPart.getSite().getPage().addPartListener(closeListener);
      }
    }
    if (mainPage.getRevertResource() != null) {
      revertException = null;
      BusyIndicator.showWhile(
          Display.getCurrent(),
          new Runnable() {
            public void run() {
              try {
                IResource[] revertResources = {mainPage.getRevertResource()};
                RevertResourcesCommand revertCommand =
                    new RevertResourcesCommand(svnResource.getWorkspaceRoot(), revertResources);
                revertCommand.run(new NullProgressMonitor());
              } catch (Exception e) {
                revertException = e;
              }
            }
          });
      if (revertException != null) {
        SVNUIPlugin.log(IStatus.ERROR, revertException.getMessage(), revertException);
        MessageDialog.openError(
            getShell(),
            Messages.ResolveTreeConflictWizard_revertError,
            revertException.getMessage());
        return false;
      }
    }
    if (mainPage.getDeleteResource() != null) {
      try {
        mainPage.getDeleteResource().delete(true, new NullProgressMonitor());
      } catch (CoreException e) {
        SVNUIPlugin.log(IStatus.ERROR, e.getMessage(), e);
        MessageDialog.openError(
            getShell(), Messages.ResolveTreeConflictWizard_deleteError, e.getMessage());
        return false;
      }
    }
    if (mainPage.getMarkResolved() || mainPage.refreshConflicts()) {
      try {
        if (mainPage.getMarkResolved()) {
          IResource[] resolvedResources = {treeConflict.getResource()};
          ResolveOperation resolveOperation =
              new ResolveOperation(
                  targetPart, resolvedResources, ISVNConflictResolver.Choice.chooseMerged) {

                protected boolean canRunAsJob() {
                  return false;
                }
              };
          resolveOperation.run();
        }

        if (mainPage.refreshConflicts()) {
          IResource[] refreshResources = {svnResource.getResource()};
          TreeConflictsView.refresh(refreshResources);
        }
      } catch (Exception e) {
        SVNUIPlugin.log(IStatus.ERROR, e.getMessage(), e);
        MessageDialog.openError(
            getShell(), Messages.ResolveTreeConflictWizard_markResolvedError, e.getMessage());
        return false;
      }
    }
    return true;
  }

  public SVNTreeConflict getTreeConflict() {
    return treeConflict;
  }

  public ISVNLocalResource getSvnResource() {
    return svnResource;
  }

  public boolean isAdded() {
    return added;
  }

  public ISVNStatus getLocalCopiedTo(boolean getAll) throws SVNException, SVNClientException {
    String endsWithCheck =
        treeConflict.getConflictDescriptor().getSrcRightVersion().getPathInRepos();
    IProject project = svnResource.getResource().getProject();
    if (project != null) {
      int index = endsWithCheck.indexOf("/" + project.getName() + "/"); // $NON-NLS-1$ //$NON-NLS-2$
      if (index != -1) endsWithCheck = endsWithCheck.substring(index);
    }
    if (copiedTo == null && !copiedToRetrieved) {
      statuses = getStatuses(getAll);
      ISVNClientAdapter svnClient = svnResource.getRepository().getSVNClient();
      for (int i = 0; i < statuses.length; i++) {
        if (statuses[i].isCopied() && statuses[i].getTextStatus().equals(SVNStatusKind.ADDED)) {
          ISVNInfo info = svnClient.getInfoFromWorkingCopy(statuses[i].getFile());
          if (info.getCopyUrl() != null) {
            if ((svnResource.getUrl() != null
                    && info.getCopyUrl().toString().equals(svnResource.getUrl().toString()))
                || (info.getCopyUrl().toString().endsWith(endsWithCheck))) {
              copiedTo = statuses[i];
              break;
            }
          }
        }
      }
      svnResource.getRepository().returnSVNClient(svnClient);
    }
    copiedToRetrieved = true;
    return copiedTo;
  }

  public ISVNStatus getRemoteCopiedTo(boolean getAll) throws Exception {
    if (remoteCopiedTo == null && !remoteCopiedToRetrieved) {
      remoteCopiedToRetrieved = true;
      logMessages = getLogMessages();
      if (logMessages != null) {
        for (int i = 0; i < logMessages.length; i++) {
          ISVNLogMessageChangePath[] changePaths = logMessages[i].getChangedPaths();
          for (int j = 0; j < changePaths.length; j++) {
            if (changePaths[j].getAction() == 'A' && changePaths[j].getCopySrcPath() != null) {
              if ((svnResource.getUrl() != null
                      && svnResource.getUrl().toString().endsWith(changePaths[j].getCopySrcPath()))
                  || changePaths[j]
                      .getCopySrcPath()
                      .endsWith(svnResource.getIResource().getFullPath().toString())) {
                statuses = getStatuses(getAll);
                for (int k = 0; k < statuses.length; k++) {
                  if (statuses[k].getUrl() != null
                      && statuses[k].getUrl().toString().endsWith(changePaths[j].getPath())) {
                    remoteCopiedTo = statuses[k];
                    return remoteCopiedTo;
                  }
                }
              }
            }
          }
        }
      }
    }
    return remoteCopiedTo;
  }

  public ISVNStatus[] getAdds() throws SVNException {
    List adds = new ArrayList();
    statuses = getStatuses(false);
    for (int i = 0; i < statuses.length; i++) {
      if (statuses[i].getTextStatus().equals(SVNStatusKind.ADDED)) adds.add(statuses[i]);
    }
    ISVNStatus[] addArray = new ISVNStatus[adds.size()];
    adds.toArray(addArray);
    return addArray;
  }

  public boolean isCompare() {
    return compare;
  }

  private ISVNStatus[] getStatuses(boolean getAll) throws SVNException {
    if (statuses == null) {
      IProject project = treeConflict.getResource().getProject();
      if (project == null) return new ISVNStatus[0];
      ISVNLocalResource svnProject = SVNWorkspaceRoot.getSVNResourceFor(project);
      GetStatusCommand command = new GetStatusCommand(svnProject, true, getAll);
      command.run(new NullProgressMonitor());
      statuses = command.getStatuses();
    }
    return statuses;
  }

  /** @return Returns the mergePath. */
  public File getMergePath() {
    return mergePath;
  }

  private ISVNLogMessage[] getLogMessages() throws Exception {
    if (logMessages == null) {
      ISVNClientAdapter svnClient = null;
      try {
        svnClient = svnResource.getRepository().getSVNClient();
        IProject project = treeConflict.getResource().getProject();
        ISVNLocalResource svnProject = SVNWorkspaceRoot.getSVNResourceFor(project);
        SVNRevision revision1 =
            new SVNRevision.Number(
                treeConflict.getConflictDescriptor().getSrcLeftVersion().getPegRevision());
        SVNRevision revision2 =
            new SVNRevision.Number(
                treeConflict.getConflictDescriptor().getSrcRightVersion().getPegRevision());
        logMessages = svnClient.getLogMessages(svnProject.getUrl(), revision1, revision2, true);
      } catch (Exception e) {
        throw e;
      } finally {
        svnResource.getRepository().returnSVNClient(svnClient);
      }
    }
    return logMessages;
  }

  class CompareCloseListener implements IPartListener2 {
    private String compareName;

    public CompareCloseListener(String compareName) {
      this.compareName = compareName;
    }

    public void partClosed(IWorkbenchPartReference partRef) {
      IWorkbenchPart part = partRef.getPart(false);
      if (part instanceof CompareEditor) {
        CompareEditor editor = (CompareEditor) part;
        IEditorInput input = editor.getEditorInput();
        String name = input.getName();
        if (name != null && name.startsWith(compareName)) {
          targetPart.getSite().getPage().removePartListener(this);
          if (MessageDialog.openQuestion(
              getShell(),
              Messages.ResolveTreeConflictWizard_editorClosed,
              Messages.ResolveTreeConflictWizard_promptToReolve
                  + treeConflict.getResource().getName()
                  + "?")) { //$NON-NLS-1$
            ResolveTreeConflictWizard wizard =
                new ResolveTreeConflictWizard(treeConflict, targetPart);
            WizardDialog dialog =
                new SizePersistedWizardDialog(
                    Display.getDefault().getActiveShell(),
                    wizard,
                    "ResolveTreeConflict"); //$NON-NLS-1$
            dialog.open();
          }
        }
      }
    }

    public void partActivated(IWorkbenchPartReference partRef) {}

    public void partBroughtToTop(IWorkbenchPartReference partRef) {}

    public void partDeactivated(IWorkbenchPartReference partRef) {}

    public void partHidden(IWorkbenchPartReference partRef) {}

    public void partInputChanged(IWorkbenchPartReference partRef) {}

    public void partOpened(IWorkbenchPartReference partRef) {}

    public void partVisible(IWorkbenchPartReference partRef) {}
  }
}
